﻿using Aliyun.Api.LOG;
using Aliyun.Api.LOG.Common.Utilities;
using Aliyun.Api.LOG.Data;
using Aliyun.Api.LOG.Request;
using Aliyun.Api.LOG.Response;
using log4net.Appender;
using log4net.Core;
using log4net.Util;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace AntJoin.Log
{
    public class LogServerAppender : AppenderSkeleton
    {
        public String Endpoint { get; set; }
        public String AccessKeyId { get; set; }
        public String AccessKeySecret { get; set; }

        public String Project { get; set; }
        public String Logstore { get; set; }
        public String Source { get; set; }
        public String Topic { get; set; }


        /// <summary>
        /// 阿里云日志服务客户端实例
        /// </summary>
        private LogClient _client;

        public LogServerAppender()
        {

        }

        public override void ActivateOptions()
        {
            base.ActivateOptions();

            try
            {
                _client = new LogClient(Endpoint, AccessKeyId, AccessKeySecret);

                LogLog.Warn(typeof(LogHelper), "AliyunLogAppender 初始化完成");
            }
            catch (Exception ex)
            {
                LogLog.Error(typeof(LogHelper), "AliyunLogAppender 初始化异常：" + ex.Message);
            }
        }


        private LogItem ConvertToLogItem(LoggingEvent loggingEvent)
        {
            LogItem logItem = new LogItem();
            //logItem.Time = LogCommon.UnixTimeSpan(loggingEvent.TimeStamp);
            logItem.Time = DateUtils.TimeSpan();
            //if (this.Layout != null)
            //{
            //    TextWriter writter = new StringWriter();
            //    this.Layout.Format(writter, loggingEvent);
            //    logItem.PushBack(loggingEvent.Level.ToString(), writter.ToString());
            //}
            //else
            //{
            //    logItem.PushBack(loggingEvent.Level.ToString(), loggingEvent.RenderedMessage);
            //}
            if (loggingEvent.MessageObject is string && !string.IsNullOrEmpty(loggingEvent.MessageObject.ToString()))
            {
                logItem.PushBack(loggingEvent.Level.ToString(), loggingEvent.MessageObject.ToString());
            }
            else if (loggingEvent.MessageObject is OperationalData)
            {
                var jobj = JObject.FromObject(loggingEvent.MessageObject);
                foreach (var property in jobj.Properties())
                {
                    var jValue = property.Value as JValue;
                    if (jValue != null)
                    {
                        var key = property.Name;
                        if (key == "Topic")
                        {
                            this.Topic = property.Value.ToString();
                        }
                    }
                    else
                    {
                        var data = property.Value as object;
                        FlattenMessageObject(data, logItem);
                    }
                }
            }
            else
            {
                FlattenMessageObject(loggingEvent.MessageObject, logItem);
            }
            if (loggingEvent.ExceptionObject != null)
            {
                logItem.PushBack("__Exception__", loggingEvent.ExceptionObject.ToString());
                logItem.PushBack("__ExceptionMessage__", loggingEvent.ExceptionObject.Message);
                logItem.PushBack("__ExceptionType__", loggingEvent.ExceptionObject.GetType().FullName);
            }
            return logItem;

        }

        /// <summary>
        /// 展开消息对象属性
        /// </summary>
        /// <param name="obj">消息对象</param>
        /// <param name="item">日志属性项目列表</param>
        /// <param name="prefix">属性前缀</param>
        private void FlattenMessageObject(object obj, LogItem item, string prefix = "")
        {
            if (obj == null)
            {
                return;
            }

            var jobj = JObject.FromObject(obj);
            foreach (var property in jobj.Properties())
            {
                var jValue = property.Value as JValue;
                if (jValue != null && jValue.Type != JTokenType.Null)
                {
                    var key = prefix + property.Name;

                    if (key == "Message")
                    {
                        key = "__Message__";
                    }

                    item.PushBack(key, property.Value != null ? property.Value.ToString() : null);
                }
            }
        }

        public void PutLog(LoggingEvent[] loggingEvents)
        {
            try
            {
                PutLogsRequest logRequest = new PutLogsRequest();
                logRequest.Project = this.Project;
                logRequest.Logstore = this.Logstore;
                logRequest.Source = this.Source;
                List<LogItem> logItems = new List<LogItem>();
                foreach (LoggingEvent item in loggingEvents)
                {
                    logItems.Add(ConvertToLogItem(item));
                }
                logRequest.Topic = this.Topic;
                logRequest.LogItems = logItems;
                LogClient client = new LogClient(this.Endpoint, this.AccessKeyId, this.AccessKeySecret);
                PutLogsResponse putLogRespError = client.PutLogs(logRequest);
            }
            catch (Exception ex)
            {
                LogLog.Error(typeof(LogHelper), "推送日志到阿里云异常：" + ex.Message);
            }
        }

        protected override void Append(LoggingEvent loggingEvent)
        {
            PutLog(new LoggingEvent[] { loggingEvent });
        }

        override protected void Append(LoggingEvent[] loggingEvents)
        {
            PutLog(loggingEvents);
        }

        /// <summary>
        /// 关闭日志写入器
        /// </summary>
        protected override void OnClose()
        {
            base.OnClose();

            _client = null;
        }
    }
}
